<?php
/**
 * Nuwani PHP IRC Bot Framework
 * Copyright (c) 2006-2010 The Nuwani Project
 *
 * Nuwani is a framework for IRC Bots built using PHP. Nuwani speeds up bot 
 * development by handling basic tasks as connection- and bot management, timers
 * and module managing. Features for your bot can easily be added by creating
 * your own modules, which will receive callbacks from the framework.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @copyright Copyright (c) 2006-2011 The Nuwani Project
 * @author Peter Beverloo <peter@lvp-media.com>
 * @author Dik Grapendaal <dik.grapendaal@gmail.com>
 * @see http://nuwani.googlecode.com
 */

/**
 * Uno Nuwani Module
 * 
 * @copyright Copyright (c) 2011 The Nuwani Project, http://nuwani.googlecode.com/
 * @author Dik Grapendaal <dik.grapendaal@gmail.com>
 * @version $Id: UnoPlayer.php 148 2011-07-26 19:10:23Z dik.grapendaal $
 * @package Modules
 */

class UnoPlayer
{
        /**
         * The ID of this player.
         * 
         * @var integer
         */
        
        private $mId;
        
        /**
         * The nickname of this player.
         * 
         * @var string
         */
        
        private $mNickname;
        
        /**
         * The cards this player currently holds.
         * 
         * @var array
         */
        
        private $mCards;
        
        /**
         * Indicates whether it's this player's turn.
         * 
         * @var boolean
         */
        
        private $mIsTurn;
        
        /**
         * Holds the internal engine representation of the card this player has
         * drawn. Used when the player tries to pass their turn or verifying
         * they play the correct card after drawing.
         * 
         * @var array
         */
        
        private $mDrawnCard;
        
        /**
         * A UnoCardStack object with the newly assigned cards since the last
         * turn.
         * 
         * @var UnoCardStack
         */
        
        private $mNewCards;
        
        /**
         * Sets up a new player object.
         * 
         * @param int $id The ID of this player.
         * @param string $nickname The name of this player.
         */
        
        public function __construct ($id, $nickname)
        {
                $this -> mId = $id;
                $this -> mNickname = $nickname;
                $this -> mIsTurn = false;
                $this -> mNewCards = new UnoCardStack ();
        }
        
        /**
         * Returns the ID of this player.
         * 
         * @return integer
         */
        
        public function getId ()
        {
                return $this -> mId;
        }
        
        /**
         * Returns this player's nickname.
         * 
         * @return string
         */
        
        public function getNickname ()
        {
                return $this -> mNickname;
        }
        
        /**
         * Checks whether the given nickname is the same as the nickname this
         * player object represents.
         * 
         * @param string $name The name to check.
         * @return boolean
         */
        
        public function nicknameEquals ($name)
        {
                return $this -> mNickname == $name;
        }
        
        /**
         * Gives a card to this player.
         * 
         * @param UnoDeckCard $card The card to give.
         * @param bool $isDrawn Whether this card was drawn by the player.
         * @return array
         */
        
        public function assignCard (UnoDeckCard $card, $isDrawn = false)
        {
                if (!($card instanceof UnoDeckCard))
                {
                        throw new Exception (UnoStrings :: INVALID_CARD);
                }
                
                $this -> mCards [] = $card;
                $card -> setLocation ($this -> mId);
                
                if (!$this -> mIsTurn)
                {
                        $this -> mNewCards -> addCard ($card -> toUnoCard ());
                }
                
                $this -> mDrawnCard = ($isDrawn) ? $card -> toUnoCard () : null;
                
                if (UnoEngine :: SETTING_SORT_CARDS)
                {
                        usort ($this -> mCards, array ('UnoCard', 'compare'));
                }
                
                return $card -> toUnoCard ();
        }
        
        /**
         * Formats all the cards this player holds using the given UnoOutput
         * object.
         * 
         * @param UnoOutput $output The UnoOutput object to use.
         * @return string
         */
        
        public function formatCards (UnoOutput $output)
        {
                $return = '';
                foreach ($this -> mCards as $card)
                {
                        $return .= $card -> format ($output) . ' ';
                }
                
                return substr ($return, 0, -1);
        }
        
        /**
         * Checks whether the player has a card with the same color and number
         * as the given UnoCard.
         * 
         * @param UnoCard $card The card properties to check.
         * @return boolean
         */
        
        public function hasCard (UnoCard $card)
        {
                foreach ($this -> mCards as $ownCard)
                {
                        if ($ownCard -> isSameCard ($card))
                        {
                                return true;
                        }
                }
                
                return false;
        }
        
        /**
         * Gets the last drawn card of this player as an UnoCard object. That
         * means that it's not played yet. Null in case of no drawn card.
         * 
         * @return UnoCard|null
         */
        
        public function getDrawnCard ()
        {
                return $this -> mDrawnCard;
        }
        
        /**
         * Returns whether this player has drawn a card. Information the engine
         * needs to know before the player is able to pass their turn.
         * 
         * @return boolean.
         */
        
        public function hasDrawnCard ()
        {
                return $this -> mDrawnCard != null;
        }
        
        /**
         * Returns an UnoCardStack object with the new cards since the last
         * turn.
         * 
         * @return UnoCardStack
         */
        
        public function getNewCards ()
        {
                return $this -> mNewCards;
        }
        
        /**
         * Indicates whether this player has new cards since the last turn.
         * 
         * @return boolean
         */
        
        public function hasNewCards ()
        {
                return count ($this -> mNewCards) > 0;
        }
        
        /**
         * Returns the number of cards this player holds.
         * 
         * @param UnoCard $card Optional card type to look for.
         * @return integer
         */
        
        public function numCards (UnoCard $card = null)
        {
                if ($card !== null)
                {
                        $num = 0;
                        foreach ($this -> mCards as $deckCard)
                        {
                                if ($deckCard -> isSameCard ($card))
                                {
                                        $num ++;
                                }
                        }
                        
                        return $num;
                }
                
                return count ($this -> mCards);
        }
        
        /**
         * Returns the amount of points this player is worth in cards.
         * 
         * @return integer
         */
        
        public function getCardValue ()
        {
                $cardValue = 0;
                foreach ($this -> mCards as $card)
                {
                        $cardValue += $card -> getCardValue ();
                }
                
                return $cardValue;
        }
        
        /**
         * Tells us that it's currently this player's turn.
         */
        
        public function notifyTurnStarted ()
        {
                $this -> mIsTurn = true;
        }
        
        /**
         * Tells us that the turn of this player has passed so that we can clean
         * up some turn specific variables.
         */
        
        public function notifyTurnPassed ()
        {
                $this -> mIsTurn = false;
                $this -> mDrawnCard = null;
                $this -> mNewCards = new UnoCardStack ();
        }
        
        /**
         * Returns a card with the same color and number as the given UnoCard,
         * effectively removing it from the player's hand.
         * 
         * @param UnoCard $card The card to find.
         * @return Card|null
         */
        
        public function playCard (UnoCard $card)
        {
                for ($i = 0; $i < count ($this -> mCards); $i ++)
                {
                        if ($this -> mCards [$i] -> isSameCard ($card))
                        {
                                $played = array_splice ($this -> mCards, $i, 1);
                                $played = array_shift ($played);
                                
                                if ($played -> isWildcard ())
                                {
                                        $played -> setNextColor ($card -> getNextColor ());
                                }
                                
                                return $played;
                        }
                }
                
                return null;
        }
        
        /**
         * Plays a stack of cards.
         * 
         * @param array $number The number we're stacking.
         * @param array $endColor The color with which the stack should end.
         * @return array|null
         */
        
        public function playStack ($number, $endColor)
        {
                $stackEnd = null;
                $stack = array ();
                
                for ($i = 0; $i < count ($this -> mCards); $i ++)
                {
                        if ($this -> mCards [$i] -> isSame ($endColor, $number) && $stackEnd == null)
                        {
                                // Left like this for array_merge() later on.
                                $stackEnd = array_splice ($this -> mCards, $i, 1);
                                $i --;
                        }
                        else if ($this -> mCards [$i] -> isSameNumber ($number))
                        {
                                $stack = array_merge ($stack, array_splice ($this -> mCards, $i, 1));
                                $i --;
                        }
                }
                
                if ($stackEnd == null)
                {
                        return array ();
                }
                
                return array_merge (
                        $stack,
                        $stackEnd
                );
        }
}